#pragma once
#include "PDTStatement.h"
#include "PDTVar.h"
#include "PDTExp.h"
#include "PDTAssign.h"

class PDTBehavior :  public PDTNode, public TraverseIfc <PDTBehavior, PDTNode> { 
    public: TRAVERSAL_IFC_IMPL(PDTBehavior)
private:
	unsigned int inSize;
	unsigned int outSize;
	bool good;
public:
	PDTBehavior(PDTCodeGraph & CG, vector <PDTStatement*> & input, vector <PDTStatement*> & output, bool aGood = true):
		PDTNode(CG), good(aGood) {
		inSize = input.size();
		outSize = output.size();

		// add input nodes first
		PDTNode * node;
		for (unsigned int i = 0; i < inSize; i++) {
			node = input[i];
			addChild(node);
			node->addParent(this);
		}

		// add output nodes second
		PDTNode * outNode;
		for (unsigned int j = 0; j < outSize; j++) {
			outNode = output[j];
			addChild(outNode);
			outNode->addParent(this);
		}
	}

	NodeTypeEnum getType() {
		return PDTGoodAssignment_Em;
	}

	virtual bool isBehavior() const {
		  return true;
	}

	bool isGood() const {
		return good;
	}

	bool isBad() const {
		return !good;
	}

	int wholeSize(){
		return inSize + outSize;
	}

    virtual unsigned int getNumInputAssignments() const {
        return inSize;
    }

    virtual PDTStatement* getInputAssignment(unsigned int  i) const{
        assert(i < inSize);
        return dynamic_cast<PDTStatement*>(children[i]);
    }

    virtual unsigned int getNumOutputAssignments() const {
        return outSize;
    }

    virtual PDTStatement* getOutputAssignment(unsigned int  i) const{
        assert(i < outSize);
        i = i+inSize;
        return dynamic_cast<PDTStatement*>(children[i]);
    }


	vector <PDTStatement*> & 
    getInput(vector <PDTStatement*> & inVec) {
		PDTStatement * assign;
		for (unsigned int i = 0; i < inSize; i++) {
			assign = dynamic_cast<PDTStatement*>(children[i]);
			inVec.push_back(assign);
		}
		return inVec;
	}

	vector <PDTStatement*> & 
    getOutput(vector <PDTStatement*> &outVec) {
		PDTStatement * assign;
		for (unsigned int i = inSize; i < inSize + outSize; i++) {
			assign = dynamic_cast<PDTStatement*>(children[i]);
			outVec.push_back(assign);
		}
		return outVec;
	}
	vector <PDTVar*> & getOutputVariables(vector <PDTVar*> & outputVariables)
	{
		vector <PDTStatement*> outVec;
		outVec = getOutput(outVec);

		for (unsigned int i = 0; i<outVec.size(); i++)
		{
			if (outVec[i]->isSequenceAssign())
			{
				PDTSequenceAssign * outVecAtI = dynamic_cast<PDTSequenceAssign*>(outVec[i]);
				outputVariables.push_back(outVecAtI->getTarget());
			}
			else if (outVec[i]->isAssign())
			{
				PDTAssign * outVecAtI = dynamic_cast<PDTAssign*>(outVec[i]);
				outputVariables.push_back(outVecAtI->getTarget());
			}
		}
		return outputVariables;
	}

	vector <PDTVar*> & getAllVariables(vector <PDTVar*> & allVariables)
	{
		vector <PDTStatement*> inVec;
		inVec = getInput(inVec);
		vector <PDTStatement*> outVec;
		outVec = getOutput(outVec);
		for (unsigned int i = 0; i<inVec.size(); i++)
		{
			if (inVec[i]->isSequenceAssign())
			{
				PDTSequenceAssign * inVecAtI = dynamic_cast<PDTSequenceAssign*>(inVec[i]);
				allVariables.push_back(inVecAtI->getTarget());
			}
			else if (inVec[i]->isAssign())
			{
				PDTAssign * inVecAtI = dynamic_cast<PDTAssign*>(inVec[i]);
				allVariables.push_back(inVecAtI->getTarget());
			}
		}

		for (unsigned int i = 0; i<outVec.size(); i++)
		{
			if (outVec[i]->isSequenceAssign())
			{
				PDTSequenceAssign * outVecAtI = dynamic_cast<PDTSequenceAssign*>(outVec[i]);
				allVariables.push_back(outVecAtI->getTarget());
			}
			else if (outVec[i]->isAssign())
			{
				PDTAssign * outVecAtI = dynamic_cast<PDTAssign*>(outVec[i]);
				allVariables.push_back(outVecAtI->getTarget());
			}
		}
		return allVariables;
	}

	vector <PDTNode*> &getAllExpressions(vector <PDTNode*> & allExpressions)
	{
		vector <PDTStatement*> inVec;
		inVec = getInput(inVec);
		vector <PDTStatement*> outVec;
		outVec = getOutput(outVec);
		for (unsigned int i = 0; i<inVec.size(); i++)
		{
			if (inVec[i]->isSequenceAssign())
			{
				PDTSequenceAssign * inVecAtI = dynamic_cast<PDTSequenceAssign*>(inVec[i]);
				PDTNode* expressionAtI = dynamic_cast<PDTNode*>(inVecAtI->getSequenceConst());
				allExpressions.push_back(expressionAtI);
			}
			else if (inVec[i]->isAssign())
			{
				PDTAssign * inVecAtI = dynamic_cast<PDTAssign*>(inVec[i]);
				PDTNode* expressionAtI = dynamic_cast<PDTNode*>(inVecAtI->getExpression());
				allExpressions.push_back(expressionAtI);
			}
		}

		for (unsigned int i = 0; i<outVec.size(); i++)
		{
			if (outVec[i]->isSequenceAssign())
			{
				PDTSequenceAssign * outVecAtI = dynamic_cast<PDTSequenceAssign*>(outVec[i]);
				PDTNode* expressionAtI = dynamic_cast<PDTNode*>(outVecAtI->getSequenceConst());
				allExpressions.push_back(expressionAtI);
			}
			else if (outVec[i]->isAssign())
			{
				PDTAssign * outVecAtI = dynamic_cast<PDTAssign*>(outVec[i]);
				PDTNode* expressionAtI = dynamic_cast<PDTNode*>(outVecAtI->getExpression());
				allExpressions.push_back(expressionAtI);
			}

		}
		return allExpressions;
	}

	virtual bool eval(PDTCodeGraph &CG)
    {
		vector <PDTStatement*> inVec;
		inVec = getInput(inVec);
		for (unsigned int i = 0; i<inSize; i++)
		{
			if (!(inVec[i]->eval(CG))) //these are PDTAssign - Evaluate them
				return false;
		}
		vector <PDTStatement*> outVec;
		outVec = getOutput(outVec);
		for (unsigned int i = 0; i<outSize; i++)
		{
			if(!(outVec[i]->eval(CG))) //these are PDTAssign - Evaluate them
				return false;
		}
		return true;
    }

	virtual bool evalInput(PDTCodeGraph &CG)
	{
		vector <PDTStatement*> inVec;
		inVec = getInput(inVec);
		for (unsigned int i = 0; i<inSize; i++)
		{
			if (!(inVec[i]->eval(CG))) //these are PDTAssign
				return false;
		}
		return true;
	}

	virtual bool evalOutput(PDTCodeGraph &CG)
    {
		vector <PDTStatement*> outVec;
		outVec = getOutput(outVec);
		for (unsigned int i = 0; i<outSize; i++)
		{
			if(!(outVec[i]->eval(CG))) //these are PDTAssign
				return false;
		}
		return true;
    }

    stringstream & inToString(stringstream & ss) const {
		PDTNode * assign=NULL;
		for (unsigned int i = 0; i < inSize - 1; i++) {
			assign = children[i];
			assign->toString(ss);
			ss << ", ";
		}
		assign = children[inSize-1];
		assign->toString(ss);
        return ss;
    }
    stringstream & outToString(stringstream & ss) const {
		PDTNode * assign=NULL;
		for (unsigned int j = inSize; j < inSize + outSize -1; j++) {
			assign = children[j];
			assign->toString(ss);
			ss << ", ";
		}
		assign = children[inSize + outSize -1];
		assign->toString(ss);
        return ss;
    }

	virtual stringstream & toString(stringstream & ss) const {
		if (good)
			ss << "good";
		else
			ss << "bad";
		ss << " { input = { ";
        inToString(ss);
		ss << "}; ";

		ss << "output = { ";
        outToString(ss);
		ss << "} }";

		return ss;
	}
};
